Skip to content

fix(config): catch parse errors gracefully during startup#3

Closed
HaleTom wants to merge 458 commits into
devfrom
json-errors
Closed

fix(config): catch parse errors gracefully during startup#3
HaleTom wants to merge 458 commits into
devfrom
json-errors

Conversation

@HaleTom

@HaleTom HaleTom commented May 25, 2026

Copy link
Copy Markdown
Owner

Closes anomalyco#29200

Type of change

  • Bug fix

What does this PR do?

Invalid JSON or JSONC in opencode.jsonc produces "Unexpected server error" on startup. ConfigParse.jsonc() throws JsonError and ConfigParse.schema() throws InvalidError as plain exceptions, which become Effect defects inside Effect.gen and propagate as generic HTTP 500s.

Wraps the parsing/schema-validation steps in Effect.sync with Effect.catchCause (the correct operator for catching sync-thrown defects inside Effect.gen). On error, logs the cause server-side and returns an empty {} config via Schema.decodeSync, so the server starts with defaults.

Plugin resolution errors are caught per-file (log + continue) so one bad file does not collapse all global config loading.

How did you verify your code works?

  • 5 new tests: invalid JSON, invalid JSONC (unterminated), invalid schema fields, multi-file partial failure, agent config still loads
  • All existing config tests pass (83 pass, 0 fail)

Related

  • anomalydg/open-code/pull/29208 (upstream PR)

kommander and others added 30 commits May 19, 2026 13:52
Co-authored-by: Jack <jack@anoma.ly>
rekram1-node and others added 20 commits May 23, 2026 20:06
…uation (anomalyco#26178)

Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
Wrap ConfigParse.jsonc() and ConfigParse.effectSchema() calls in
loadConfig with Effect.catchCause to prevent sync-thrown JsonError
and InvalidError from becoming Effect defects. On parse/schema
failure, the bad file is skipped with a log.error and {} fallback
instead of crashing, matching the existing tui.jsonc error handling
pattern in tui.ts.

Without this, any invalid JSONC syntax or schema violation in
opencode.json/opencode.jsonc causes "4 of 6 requests failed:
Unexpected server error" on startup. Now the server starts with
default values and logs the config error details.

Fixes anomalyco#29200
Copilot AI review requested due to automatic review settings May 25, 2026 14:06
@semanticdiff-com

Copy link
Copy Markdown

Review changes with  SemanticDiff

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

@github-actions

Copy link
Copy Markdown

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@HaleTom HaleTom requested a review from Copilot May 25, 2026 14:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

@HaleTom

HaleTom commented May 25, 2026

Copy link
Copy Markdown
Owner Author

@gemini-code-review pls review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the application's sync and SDK layers, transitioning "Global" contexts to "Server" and introducing directory-specific synchronization. The desktop build system is migrated from Tauri to Electron, and the session timeline is updated with virtualization. A new "v2" UI design for the prompt input and titlebar is introduced for non-production environments. Additionally, a referral system is added to the console, infrastructure monitoring is updated, and Ukrainian translations are provided. Feedback identifies a missing luxon import and a logic error in directory matching. Recommendations are also made to improve the virtualized timeline's row preservation, accessibility for screen readers, and state management when navigating between sessions.

}

function groupSessions(records: HomeSessionRecord[], language: ReturnType<typeof useLanguage>): HomeSessionGroup[] {
const now = DateTime.local()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The DateTime class from luxon is used here but it is not imported in this file. This will cause a ReferenceError at runtime.


const current = createMemo(() => serverSync.child(directory))
const target = (directory?: string) => {
if (!directory || directory === directory) return current()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The condition directory === directory is a tautology because the parameter directory shadows the outer scope variable of the same name. This makes the subsequent code on line 178 unreachable. You should rename the parameter or use a different check to determine if the requested directory matches the current one.

    if (!directory || directory === current()[0].path.directory) return current()

Comment on lines +449 to +456
const keepMounted = createMemo(() => {
const id = activeMessageID()
if (!id) return
const rows = timelineRows()
const index = rows.findLastIndex((row) => "userMessageID" in row && row.userMessageID === id)
if (index < 0) return
return [index]
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The keepMounted logic currently only preserves the last row of the active turn. Since a turn can consist of multiple rows (UserMessage, multiple AssistantParts, Thinking, etc.), scrolling up while a turn is active might cause earlier parts of the streaming response to be unmounted by the virtualizer. This can disrupt the streaming experience and stateful animations like TextReveal. It is recommended to keep all rows associated with the active userMessageID mounted.

  const keepMounted = createMemo(() => {
    const id = activeMessageID()
    if (!id) return
    const rows = timelineRows()
    const indices: number[] = []
    rows.forEach((row, index) => {
      if ("userMessageID" in row && row.userMessageID === id) {
        indices.push(index)
      }
    })
    return indices.length > 0 ? indices : undefined
  })

}}
data-slot="session-turn-assistant-content"
aria-hidden={workingTurn(assistantPartRow().userMessageID)}
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Setting aria-hidden to true while the turn is busy prevents screen readers from accessing the assistant's response as it streams in. This significantly degrades the experience for users relying on assistive technologies. Consider removing this attribute or using aria-live to announce updates.

const [bar, setBar] = createStore({
ms: pace(640),
})
const [toolOpen, setToolOpen] = createStore<Record<string, boolean | undefined>>({})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The toolOpen store persists state for the lifetime of the MessageTimeline component. When switching between sessions, this state is not cleared, which could lead to incorrect expanded/collapsed states if part IDs happen to collide or simply unnecessary memory growth. Consider clearing this store when the sessionKey changes.

@HaleTom HaleTom closed this May 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invalid JSON/C causes "Unexpected server error" on startup